#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>
 
#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"

#include "sm2.h"

#include "user_define.h"
#include "record_define.h"
#include "crypt_define.h"
// add para lib_include
int crypt_manage_init(void * sub_proc, void * para)
{
	int ret;
	// add yorself's module init func here
	return 0;
}
int crypt_manage_start(void * sub_proc, void * para)
{
	int ret;
	void * recv_msg;
	int type;
	int subtype;
	// add yorself's module exec func here
	while(1)
	{
		usleep(time_val.tv_usec);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0)
			continue;
		if(recv_msg==NULL)
			continue;
		type=message_get_type(recv_msg);
		subtype=message_get_subtype(recv_msg);
		if(!memdb_find_recordtype(type,subtype))
		{
			printf("message format (%d %d) is not registered!\n",
			message_get_type(recv_msg),message_get_subtype(recv_msg));
			continue;
		}
		if((type==TYPE(USER_DEFINE))&&(subtype==SUBTYPE(USER_DEFINE,LOGIN)))
		{
			ret=proc_login_crypt_user(sub_proc,recv_msg);
		}
		else if((type==TYPE(USER_DEFINE))&&(subtype==SUBTYPE(USER_DEFINE,RETURN)))
		{
			ret=proc_login_crypt_check(sub_proc,recv_msg);
		}
		else if((type==TYPE(GENERAL_RETURN))&&(subtype==SUBTYPE(GENERAL_RETURN,STRING)))
		{
			ret=proc_login_crypt_manage(sub_proc,recv_msg);
		}
		else if((type==TYPE(RECORD_DEFINE))&&(subtype==SUBTYPE(RECORD_DEFINE,GOODS_INFO)))
		{
			ret=proc_order_signverify(sub_proc,recv_msg);
		}
	}
	return 0;
}

int proc_login_crypt_user(void * sub_proc,void * recv_msg)
// this function build relationship between connected instance and user
{
    int ret=0;
	RECORD(CRYPT_DEFINE,CONN_USER) * conn_user;
	RECORD(USER_DEFINE,LOGIN) * user_login;
    BYTE * proc_uuid;
	BYTE comp_uuid[DIGEST_SIZE];

	ret=message_get_record(recv_msg,&user_login,0);
	if(ret<0)
		return ret;
	proc_uuid = message_get_sender(recv_msg);
    conn_user=Talloc0(sizeof(*conn_user));
    if(conn_user==NULL)
        return -ENOMEM;
    Memcpy(conn_user->proc_uuid,proc_uuid,DIGEST_SIZE);
    conn_user->user_name=dup_str(user_login->user_name,DIGEST_SIZE);
    memdb_store(conn_user,TYPE_PAIR(CRYPT_DEFINE,CONN_USER),user_login->user_name);
 
    ret = ex_module_sendmsg(sub_proc,recv_msg);
    return ret;
}


int proc_login_crypt_check(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(USER_DEFINE,RETURN) * login_return;
	RECORD(CRYPT_DEFINE,LOCAL_KEYSET) * local_keyset;
	RECORD(CRYPT_DEFINE,PUBLIC_KEY) * public_key;
	RECORD(CRYPT_DEFINE,CONN_USER) * conn_user;
	BYTE * conn_uuid;
	DB_RECORD * db_record;
	MSG_EXPAND * msg_expand;

	ret=message_get_record(recv_msg,&login_return,0);
	if(ret<0)
		return ret;
    if(login_return->return_code != 1)
    {
        print_cubeerr("user login failed!\n");
        return -EINVAL;
    }    
	conn_uuid = message_get_sender(recv_msg);
    
    db_record = memdb_find_first(TYPE_PAIR(CRYPT_DEFINE,CONN_USER),"proc_uuid",conn_uuid);

    if(db_record==NULL)
    {
        print_cubeerr("can't find legal conn user!");
        return -EINVAL;
    }
    conn_user=db_record->record;
    conn_user->isverified=1;
    memdb_store(conn_user,TYPE_PAIR(CRYPT_DEFINE,CONN_USER),conn_user->user_name);

	db_record=memdb_find_first(TYPE_PAIR(CRYPT_DEFINE,LOCAL_KEYSET),"user_name",conn_user->user_name);
	if(db_record==NULL)
	{
		// find nothing, create local keyset
		local_keyset=Talloc0(sizeof(*local_keyset));
		if(local_keyset==NULL)
			return -ENOMEM;
		Memcpy(local_keyset->node_uuid,local_keyset,DIGEST_SIZE);
		local_keyset->user_name=dup_str(conn_user->user_name,DIGEST_SIZE);
		printf("create new pubkey pair for user %s!\n",local_keyset->user_name);
	
		ret=_create_local_keyset(local_keyset);
		if(ret<0)
		{
			printf("create %s's local keyset failed!\n",local_keyset->user_name);
		}

		memdb_store(local_keyset,TYPE_PAIR(CRYPT_DEFINE,LOCAL_KEYSET),NULL);
		void * type_msg = create_type_message(TYPE_PAIR(CRYPT_DEFINE,LOCAL_KEYSET),NULL);
		ex_module_sendmsg(sub_proc,type_msg);
	}
	else
	{
		// find the localkeyset
		print_cubeaudit("user %s has local keyset!\n",conn_user->user_name);
		local_keyset=db_record->record;
	}
	ret=ex_module_sendmsg(sub_proc,recv_msg);
	return ret;
}
int proc_login_crypt_manage(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(GENERAL_RETURN,STRING) * manage_cmd;
	RECORD(CRYPT_DEFINE,LOCAL_KEYSET) * local_keyset;
	RECORD(CRYPT_DEFINE,PUBLIC_KEY) * public_key;
	RECORD(CRYPT_DEFINE,CONN_USER) * conn_user;
	BYTE * conn_uuid;
	DB_RECORD * db_record;
    MSG_EXPAND * msg_expand;

    ret=message_get_record(recv_msg,&manage_cmd,0);
    if(manage_cmd == NULL)
        return -EINVAL;
    
    if(Strcmp(manage_cmd->return_value,"sendkey")!=0)
    {
        print_cubeerr("crypt manage: unknown cmd %s",manage_cmd->return_value);
        return -EINVAL;
    }
    
	ret = message_get_define_expand(recv_msg,&msg_expand,TYPE_PAIR(CRYPT_DEFINE,PUBLIC_KEY));
	if(ret<0)
		return ret;
	if(msg_expand == NULL)	// create pubkey struct 
    {
	    conn_uuid = message_get_sender(recv_msg);
    
        db_record = memdb_find_first(TYPE_PAIR(CRYPT_DEFINE,CONN_USER),"proc_uuid",conn_uuid);

        if(db_record==NULL)
        {
            print_cubeerr("can't find legal conn user!");
            return -EINVAL;
        }
        conn_user=db_record->record;
		//search user's local keyset

	    db_record=memdb_find_first(TYPE_PAIR(CRYPT_DEFINE,LOCAL_KEYSET),"user_name",conn_user->user_name);
	    if(db_record==NULL)
	    {
			print_cubeerr("user %s has no local keyset",conn_user->user_name);
            return -EINVAL;
        }

        local_keyset=db_record->record;
		public_key=Talloc0(sizeof(*public_key));
		Memcpy(public_key->node_uuid,local_keyset->node_uuid,DIGEST_SIZE);
		public_key->user_name=dup_str(local_keyset->user_name,DIGEST_SIZE);
		public_key->pubkey_len=local_keyset->pubkey_len;
        public_key->pubkey=Talloc0(public_key->pubkey_len);
        Memcpy(public_key->pubkey,local_keyset->pubkey,local_keyset->pubkey_len);
	    
        message_add_expand_data(recv_msg,TYPE_PAIR(CRYPT_DEFINE,PUBLIC_KEY),public_key);
    }
    else
    {
        public_key=msg_expand->expand;
		memdb_store(public_key,TYPE_PAIR(CRYPT_DEFINE,PUBLIC_KEY),public_key->user_name);
		void * type_msg = create_type_message(TYPE_PAIR(CRYPT_DEFINE,PUBLIC_KEY),NULL);
		ex_module_sendmsg(sub_proc,type_msg);
	
		// create public key struct
	}
    ex_module_sendmsg(sub_proc,recv_msg);   

}
int proc_order_signverify(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(CRYPT_DEFINE,LOCAL_KEYSET) * local_keyset;
	RECORD(CRYPT_DEFINE,PUBLIC_KEY) * public_key;
	RECORD(CRYPT_DEFINE,DATA_SIGN) * sign_data;
	RECORD(CRYPT_DEFINE,CONN_USER) * conn_user;
	MSG_EXPAND * msg_expand;
	DB_RECORD * db_record;
	BYTE *conn_uuid;


	ret = message_get_define_expand(recv_msg,&msg_expand,TYPE_PAIR(CRYPT_DEFINE,DATA_SIGN));
	if(ret<0)
		return ret;
	if(msg_expand == NULL)	// sign data
	{
	    conn_uuid = message_get_sender(recv_msg);
    
        db_record = memdb_find_first(TYPE_PAIR(CRYPT_DEFINE,CONN_USER),"proc_uuid",conn_uuid);

        if(db_record==NULL)
        {
            print_cubeerr("can't find legal conn user!");
            return -EINVAL;
        }
        conn_user=db_record->record;
		//search user's local keyset
		db_record=memdb_find_first(TYPE_PAIR(CRYPT_DEFINE,LOCAL_KEYSET),"user_name",conn_user->user_name);
		if(db_record==NULL)
		{
			print_cubeerr("crypt_manage: can't find user %s's keyset!",conn_user->user_name);
			return -EINVAL;
		}
		local_keyset=db_record->record;
		ret=message_add_expand_data(recv_msg,TYPE_PAIR(CRYPT_DEFINE,LOCAL_KEYSET),local_keyset);
		if(ret<0)
			return ret;
	}
	else     // verify data
	{
        sign_data=msg_expand->expand;
		db_record=memdb_find_first(TYPE_PAIR(CRYPT_DEFINE,PUBLIC_KEY),"user_name",sign_data->user_name);
		if(db_record==NULL)
		{
			print_cubeerr("crypt_manage: can't find user %s's public key!",sign_data->user_name);
			return -EINVAL;
		}
		public_key=db_record->record;
		ret=message_add_expand_data(recv_msg,TYPE_PAIR(CRYPT_DEFINE,PUBLIC_KEY),public_key);
		if(ret<0)
			return ret;
		
	}
	
	ret=ex_module_sendmsg(sub_proc,recv_msg);
	return ret;
}

int _create_local_keyset( RECORD(CRYPT_DEFINE,LOCAL_KEYSET) * local_keyset)
{
	int ret;
	BYTE privkey_buf[128];
	long long privkey_len;
	BYTE pubkey_buf[128];
	int pubkey_len=64;
       	ret=GM_GenSM2keypair(privkey_buf,&privkey_len,pubkey_buf);
       	if(ret!=0)
		return -EINVAL;
	local_keyset->pubkey_len=pubkey_len;
	local_keyset->pubkey=Talloc0(pubkey_len);
	Memcpy(local_keyset->pubkey,pubkey_buf,pubkey_len);

	local_keyset->privkey_len=privkey_len;
	local_keyset->privkey=Talloc0(privkey_len);
	Memcpy(local_keyset->privkey,privkey_buf,privkey_len);
	Memset(pubkey_buf,0,sizeof(pubkey_buf));
	Memset(privkey_buf,0,sizeof(privkey_buf));
	return 0;
}
